#    Copyright 2012 New Mexico EPSCoR
#
#    This file is part of Teacher's P.E.T.
#
#    Teacher's P.E.T. is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Teacher's P.E.T. is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Teacher's P.E.T..  If not, see <http://www.gnu.org/licenses/>.

from django.shortcuts import render_to_response, RequestContext, Http404, HttpResponseRedirect, get_object_or_404
from django.contrib import messages
from django.core.paginator import EmptyPage, PageNotAnInteger
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType

from apps.forum.models import *
from apps.course.models import Course
from apps.participants.models import CourseParticipant
from apps.course.functions import course_reverse
from apps import files

import logging

logger = logging.getLogger('pet.apps.forum.views')

def send_pm(request, course=None, reply_to=None, draft_id=None, reply_type='reply'):
    logger.fxn_entry("Course %s: User %s sending pm (reply to id %s)",course, request.user,reply_to)
    
    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to send pm in forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    TheMessageRepliedTo = None
    if reply_to:
        TheMessageRepliedTo = ForumPrivateMessage.objects.get(id=reply_to)
        if not TheMessageRepliedTo or not TheMessageRepliedTo.recipient == TheCourseParticipant.user or TheMessageRepliedTo.is_sent(False):
            logger.forced_redirect("User %s trying to reply to unfindable message with id %s, message not to them, or message is draft",TheCourseParticipant,reply_to)
            messages.info(request,"Message not found");
            return HttpResponseRedirect(course_reverse('apps.forum.views.view_threads',TheCourse))
        
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_REPLIES and TheMessageRepliedTo:
        logger.forced_redirect("User %s banned from replying to messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to new messages (including replies). Ban in effect");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
    elif Bans and Bans[0].message_ban >= ForumBan.BAN_NEW_MESSAGES:
        logger.forced_redirect("User %s banned from composing messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to new messages. Ban in effect");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))

    TheMessage = None
    if draft_id:
        TheMessage = ForumPrivateMessage.objects.get(pk=draft_id)
        if not TheMessage or TheMessage.sender != TheCourseParticipant.user or TheMessage.forum != TheForum:
            messages.info(request,"Message not found")
            TheMessage = None

    Recipient = None
    if TheMessage:
        Recipient = TheMessage.recipient
    else:
        TheMessage = ForumPrivateMessage(sender=TheCourseParticipant.user,forum=TheForum)
        if TheMessageRepliedTo:
            TheMessage.message = "<br><label for='reply%s'>On %s, %s wrote:</label>" % (TheMessageRepliedTo.id,TheMessageRepliedTo.send_date.strftime("%a, %b %d,%Y at %I:%M %p"), TheMessageRepliedTo.sender)
            TheMessage.message += "<br><blockquote id='reply%s'>%s</blockquote>" % (TheMessageRepliedTo.id,TheMessageRepliedTo.message)
            if reply_type == "reply":
                Recipient = TheMessageRepliedTo.sender
                TheMessage.subject = "Re: " + TheMessageRepliedTo.subject
            elif reply_type == "forward":
                TheMessage.subject = "Fwd: " + TheMessageRepliedTo.subject
    #If we have a GET request, this came from a "Send SomeUser a Message" link

    if request.method == "GET":
        if 'recipient' in request.GET:
            recipient = User.objects.filter(username=request.GET['recipient'])
            if recipient:
                for course in TheForum.courses.all():
                    TheRecipient = CourseParticipant.find(course,recipient[0])
                    if TheRecipient: Recipient = TheRecipient.user; break
    #If we get a POST request, this came from the PrivateMessage Form
    if request.method == "POST":
        Error = False
        form = ForumPrivateMessageForm(request.POST,sender=TheCourseParticipant.user,instance=TheMessage)
        if form.is_valid():
            if form.has_changed() or "Send" in request.POST or "Attach" in request.POST:
                form.instance.forum = TheForum
                form.instance.sender = TheCourseParticipant.user
                NewMessage = form.save()
                if "Send" in request.POST:
                    NewMessage.mark_sent(); NewMessage.save()
                    messages.info(request,"Message sent to %s" % form.cleaned_data['recipient'])
                    logger.info("Sending message")
                elif "Draft" in request.POST:
                    messages.info(request,"Message saved")
                    logger.info("Saving message as draft")
                elif "Attach" in request.POST:
                    if NewMessage.is_sent(False):
                        logger.info("Saving message and redirecting to attachment page")
                        return HttpResponseRedirect(course_reverse('forum_attach_draft',TheCourse,draft_id=NewMessage.id))
                    logger.info("Cannot attach files to sent message, %s" % request.user)
                return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
            else: logger.form_unchanged("ForumPrivateMessageForm unchanged")
        else: Error = True; logger.error("ForumPrivateMessageForm invalid - %s" % form)
    #Otherwise, this is from a generic "Compose" link
    else:
        logger.info("Creating generic compose form with message %s to initial %s",TheMessage,Recipient)
        form = ForumPrivateMessageForm(sender=TheCourseParticipant.user,instance=TheMessage,initial={'recipient':Recipient})

    logger.fxn_exit("Returning")
    return render_to_response('forum/message/send_pm.html', {'form': form}, context_instance = RequestContext(request))

def view_messages(request, course=None, folder='inbox', page=1):
    logger.fxn_entry("Course %s: User %s viewing messages",course, request.user)

    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to view messages in forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGES:
        logger.forced_redirect("User %s banned from viewing messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to messages. Ban in effect");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    block_list = request.user.block_list.all()
    
    if folder == 'sent':
        message_list = ForumPrivateMessage.objects.filter(sender=TheCourseParticipant.user,send_date__isnull=False,hide_date__isnull=True).order_by('-send_date')
    elif folder == 'drafts':
        message_list = ForumPrivateMessage.objects.filter(sender=TheCourseParticipant.user,send_date__isnull=True)
    elif folder == 'trash':
        message_list = request.user.received_messages.filter(send_date__isnull=False,deleted_date__isnull=False,hide_date__isnull=True).order_by('-send_date').exclude(sender__in=block_list)
    else:
        message_list = request.user.received_messages.filter(send_date__isnull=False,deleted_date__isnull=True,hide_date__isnull=True).order_by('-send_date').exclude(sender__in=block_list)
        
    paginator = EmptyPaginator(message_list, 20) #Show 20 entries per page
    try:
        message_list = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        message_list = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        message_list = paginator.page(paginator.num_pages)
    
    message_list.pages_left = paginator.num_pages - message_list.number
    logger.fxn_exit("Listing messages")
    return render_to_response('forum/message/list_messages.html', {"page": message_list,'folder':folder,'ban':Bans[0] if Bans else None}, context_instance = RequestContext(request))

def view_message(request, course=None, message_id=None):
    logger.fxn_entry("Course %s: User %s viewing message %s",course, request.user, message_id)

    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum

    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to view messages in forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGES:
        logger.forced_redirect("User %s banned from viewing messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to messages. Ban in effect");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    TheMessage = ForumPrivateMessage.objects.get(id=message_id)
    if not TheMessage:
        logger.http404("User %s trying to view invalid message %s",request.user,message_id)
        raise Http404
    if TheCourseParticipant.user.block_list.filter(blocked=TheMessage.sender):
        messages.info("Message sender blocked. Unable to display message")
        logger.forced_redirect("User %s requested message %s from blocked user %s. Access denied" % (TheCourseParticipant.user,message_id,TheMessage.sender))
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
        
    if not TheCourseParticipant.is_instructor() and not TheCourseParticipant.is_assistant():
        if TheMessage.is_hidden():
            logger.forced_redirect("User %s trying to view message %s that was hidden in forum '%s'",request.user,message_id,TheForum)
            messages.info(request,"Message not found");
            return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
        if TheMessage.sender != request.user and TheMessage.recipient != request.user:
            logger.forced_redirect("User %s trying to view message %s that was neither received nor sent in forum '%s'",request.user,message_id,TheForum)
            messages.info(request,"Message not found");
            return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
        if TheMessage.is_sent(False):
            if TheMessage.sender == request.user:
                logger.fxn_exit("Message %s is draft, returning send_pm()",message_id)
                return send_pm(request=request, course=course, draft_id=message_id)
            else:
                logger.http404("User %s trying to view user %s's unsent message %s",request.user,TheMessage.sender,message_id)
                raise Http404
    
    TheMessage.can_be_unread = True
    TheMessage.can_be_deleted = True
    TheMessage.can_be_undeleted = False
    TheMessage.can_be_blocked = False
    TheMessage.can_be_flagged = False
    
    if TheMessage.is_sent(False):
        TheMessage.can_be_unread = False
    elif TheMessage.sender == request.user and TheMessage.recipient != request.user:
        TheMessage.can_be_unread = False
        TheMessage.can_be_deleted = False
    elif TheMessage.recipient == request.user:
        TheMessage.mark_read(); TheMessage.save()
        TheMessage.can_be_blocked = True if not TheCourseParticipant.is_instructor() and not TheCourseParticipant.is_assistant() else False
        TheMessage.can_be_flagged = True
        if TheMessage.is_deleted():
            TheMessage.can_be_unread = False
            TheMessage.can_be_deleted = False
            TheMessage.can_be_undeleted = True
        
    logger.fxn_exit("Displaying message")
    return render_to_response('forum/message/view_message.html', {"message": TheMessage}, context_instance = RequestContext(request))
    

def mark_message(request, course=None, message_id=None, option=None):
    logger.fxn_entry("Course %s: User %s marking message %s as %s",course, request.user, message_id, option)

    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    TheMessage = request.user.received_messages.filter(id=message_id)
    if TheMessage: TheMessage = TheMessage[0]
    else:
        TheMessage = ForumPrivateMessage.objects.get(id=message_id)
        if not TheMessage:
            logger.http404("User %s trying to view invalid message %s",request.user,message_id)
            return Http404
        if TheMessage.sender != request.user:
            logger.forced_redirect("User %s trying to view message %s that was neither received nor sent in forum '%s'",request.user,message_id,TheForum)
            messages.info(request,"Access to this part of the site requires course authentication");
            return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to view messages in forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGES:
        logger.forced_redirect("User %s banned from viewing messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to messages. Ban in effect");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))

    if option == "unread" and TheMessage.is_read():
        TheMessage.mark_unread()
        TheMessage.save()
        messages.info(request,"Message marked unread")
    elif option == "delete":
        if request.user == TheMessage.sender and TheMessage.is_sent(False):
            TheMessage.delete();
            messages.info(request,"Message discarded")
        elif TheMessage.recipient == request.user and TheMessage.is_sent():
            TheMessage.mark_deleted(); TheMessage.save();
            messages.info(request,"Message moved to Trash")
    elif option == "undelete":
        if TheMessage.recipient == request.user and TheMessage.is_sent() and TheMessage.is_deleted():
            TheMessage.mark_undeleted(); TheMessage.save();
            messages.info(request,"Message moved to Inbox")
    elif option == "report":
        TheMessage.mark_flagged_for_moderator(); TheMessage.save()
        messages.info(request,"Message marked for review")
    elif option == "block":
        if not request.user.block_list.filter(blocked=TheMessage.sender):
            NewBlock = ForumPrivateMessageBlock(blocker=request.user,blocked=TheMessage.sender)
            NewBlock.save()
            messages.info(request,"User %s %s blocked. No messages can be sent, seen, or received from this user." % (TheMessage.sender.first_name, TheMessage.sender.last_name))
        else:
            messages.info(request,"User %s %s already blocked." % (TheMessage.sender.first_name, TheMessage.sender.last_name))
    elif option == "unblock":
        TheBlock = request.user.block_list.filter(blocked=TheMessage.sender)
        if TheBlock:
            TheBlock[0].delete()
            messages.info(request,"User %s %s unblocked. Messages can now be sent, seen, and received from this user." % (TheMessage.sender.first_name, TheMessage.sender.last_name))
        else:
            messages.info(request,"User %s %s wasn't blocked." % (TheMessage.sender.first_name, TheMessage.sender.last_name))
        
    logger.fxn_exit("Message marked")
    return HttpResponseRedirect(course_reverse('forum_view_messages'))
    
def attach_draft(request,course=None,draft_id=None):
    logger.fxn_entry("course=%s,draft_id=%s",course,draft_id)
    
    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum

    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to access forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGE_ATTACHMENTS:
        logger.forced_redirect("User %s banned managing message attachments in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access Denied. Ban in effect");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
    
    TheDraft = ForumPrivateMessage.objects.get(sender=request.user,pk=draft_id)
    #figure out what the user is trying to do and if they gave us good data
    if not TheDraft:
        logger.forced_redirect("User %s attempted to edit invalid draft %s in Forum %s",TheCourseParticipant,draft_id,TheForum)
        messages.info(request,"Invalid draft specified");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
    if TheDraft and TheDraft.sender != TheCourseParticipant.user:
        logger.forced_redirect("User %s attempted to edit %s's draft %s in Forum %s",TheCourseParticipant,TheDraft.sender, TheDraft,TheForum)
        messages.error(request,"Invalid draft specified");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))

    logger.fxn_exit("Returning")
    return render_to_response('forum/message/attach_draft.html', {"TheForum":TheForum, "TheMessage":TheDraft}, context_instance = RequestContext(request))

def download_message_attachment(request, course=None, message_id=None, filename=None):
    logger.fxn_entry("course %s,message_id %s,filename %s",course,message_id,filename)
    
    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    TheMessage = ForumPrivateMessage.objects.get(pk=message_id)
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to access message '%s' attachment",TheMessage)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGES:
        logger.forced_redirect("User %s banned from downloading message attachments in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access Denied. Ban in effect");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    if not TheMessage:
        logger.forced_redirect("User %s attempted to download from invalid message %s in Forum %s",TheCourseParticipant,message_id,TheForum)
        messages.info(request,"Invalid post specified, please try again");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))

    return files.views.files.serve_file(request,ContentType.objects.get_for_model(TheMessage),TheMessage.pk,filename)

def delete_message_attachment(request, course=None, message_id=None, file_id=None):
    logger.fxn_entry("course %s,message_id %s,file_id %s",course,message_id,file_id)
    
    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    TheMessage = ForumPrivateMessage.objects.get(pk=message_id)
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to access message forum '%s' attachment",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGE_ATTACHMENTS:
        logger.forced_redirect("User %s banned from message attachments in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access Denied. Ban in effect");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
    
    if not TheMessage:
        logger.forced_redirect("User %s attempted to delete an attachment from invalid message %s in Forum %s",TheCourseParticipant,message_id,TheForum)
        messages.info(request,"Invalid post specified, please try again");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))

    if TheMessage.is_sent():
        logger.forced_redirect("User %s attempted to delete an attachment from a sent message %s in Forum %s",TheCourseParticipant,message_id,TheForum)
        messages.info(request,"Attachments cannot be deleted from a sent message");
        return HttpResponseRedirect(course_reverse('forum_view_messages',TheCourse))
        
    file = get_object_or_404(files.get_model(), pk=file_id)
    file.delete()
    messages.info(request,"Attachment deleted")
    logger.fxn_exit('Returning to attach files after deleting file')
    return HttpResponseRedirect(course_reverse('forum_attach_draft',TheCourse,draft_id=message_id))

def view_blocklist(request,course=None):
    logger.fxn_entry("Course %s: User %s viewing blocklist",course, request.user)

    TheCourse = Course.find(course)
    TheCourseParticipant = CourseParticipant.find(TheCourse,request.user)
    TheForum = TheCourse.forum
    
    if not TheCourseParticipant:
        logger.forced_redirect("Anonymous user trying to view blocklist in forum '%s'",TheForum)
        messages.info(request,"Access to this part of the site requires course authentication");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))
    
    Bans = ForumBan.objects.filter(forum=TheForum,course_participant=TheCourseParticipant)
    if Bans and Bans[0].message_ban >= ForumBan.BAN_MESSAGES:
        logger.forced_redirect("User %s banned from viewing messages in forum '%s'",TheCourseParticipant,TheForum)
        messages.info(request,"Access denied to messages. Ban in effect");
        return HttpResponseRedirect(course_reverse('course_index',TheCourse))

    BlockList = TheCourseParticipant.user.block_list.all()
    #Find messages from each person (it makes it easier to include blocking/unblocking in the alter_message view)
    for block in BlockList:
        msg = ForumPrivateMessage.objects.filter(sender=block.blocked,recipient=block.blocker)
        if msg: block.message = msg[0]
        else: logger.error("Error finding message with sender %s and recipient %s when existence indicated by %s's blocklist" % (block.blocked,block.blocker,block.blocker))
        
    logger.fxn_exit("Displaying BlockList")
    return render_to_response('forum/message/view_blocklist.html', {"TheForum":TheForum, "BlockList":BlockList}, context_instance = RequestContext(request))
